package handler

import (
	"fmt"
	"github.com/pkg/sftp"
	"go-devops/common"
	"go-devops/domain/dto"
	"golang.org/x/crypto/ssh"
	"io"
	"log"
	"os"
	"path/filepath"
	"strings"
	"time"
)

type SFTPTaskHandler struct {
	CommonTaskHandler
}

func NewSFTPTaskHandler() *SFTPTaskHandler {
	return &SFTPTaskHandler{
		CommonTaskHandler: CommonTaskHandler{},
	}
}

type SFTPData struct {
	Command    string     `json:"command"`
	Host       string     `json:"host"`
	Port       int        `json:"port"`
	SecretName string     `json:"secretName"`
	WorkDir    string     `json:"workDir"`
	FileList   []SFTPFile `json:"fileList"`
}

type SFTPFile struct {
	Id         string `json:"id"`
	LocalPath  string `json:"localPath"`
	RemotePath string `json:"remotePath"`
}

func getSFTPData(context *dto.NodeContext) SFTPData {
	data := context.TaskData.Data
	jsonString := common.JsonString(data)
	var sftpData SFTPData
	common.ParseJson(jsonString, &sftpData)
	return sftpData
}

func (c SFTPTaskHandler) Stop(context *dto.NodeContext) error {
	context.StopFlag = true
	context.Cancel()
	return nil
}

func (c SFTPTaskHandler) HandleNode(nodeContext *dto.NodeContext) error {
	return c.Handle(nodeContext, func() error {
		workspace := nodeContext.Workspace
		log.Println("工作空间：" + workspace)
		sendMsg(nodeContext, true, fmt.Sprintf("工作空间：%s", workspace))

		data := getSFTPData(nodeContext)
		log.Printf("SFTPTaskHandler data:%+v\n", data)
		dataJson := common.JsonString(data)
		sendMsg(nodeContext, true, fmt.Sprintf("taskData:%s", dataJson))

		secret, err := GetSecret(common.SSH, data.SecretName)
		if err != nil {
			return err
		}
		log.Printf("SFTPTaskHandler secret:+%v\n", secret)

		// 启动命令
		sendMsg(nodeContext, true, "开始执行处理：--------------------------------------->")
		startHandleTime := time.Now()

		server := fmt.Sprintf("%s:%d", data.Host, data.Port)

		// 创建 SSH 客户端配置
		config := &ssh.ClientConfig{
			User: secret.SecretKey,
			Auth: []ssh.AuthMethod{
				ssh.Password(secret.SecretValue),
			},
			HostKeyCallback: ssh.InsecureIgnoreHostKey(),
		}

		// 连接到 SSH 服务器
		conn, err := ssh.Dial("tcp", server, config)
		if err != nil {
			log.Printf("Failed to dial: %v\n", err)
			return fmt.Errorf("ssh conn error: %v", err)
		}
		defer conn.Close()

		sftpClient, err := sftp.NewClient(conn)
		if err != nil {
			log.Printf("Failed to sftp client: %v\n", err)
			return fmt.Errorf("sftp conn error: %v", err)
		}
		defer sftpClient.Close()

		remoteWorkDir, err := sftpClient.Getwd()
		if err != nil {
			log.Printf("Failed to get remoteWorkDir: %v\n", err)
			return fmt.Errorf("sftp get remoteWorkDir error: %v", err)
		}
		log.Printf("SFTPTaskHandler remoteWorkDir:%v\n", remoteWorkDir)
		sendMsg(nodeContext, true, fmt.Sprintf("远程工作地址：%s", remoteWorkDir))

		if len(data.FileList) <= 0 {
			return fmt.Errorf("传输文件列表为空")
		}

		for _, item := range data.FileList {
			if item.LocalPath == "" || len(item.LocalPath) <= 0 {
				return fmt.Errorf("本地文件地址为空")
			}
			if item.RemotePath == "" || len(item.RemotePath) <= 0 {
				return fmt.Errorf("远程文件地址为空")
			}

			if strings.Contains(item.LocalPath, "*") {
				if err := handleMatchFile(item.LocalPath, item.RemotePath, nodeContext, sftpClient); err != nil {
					return err
				}
			} else {
				if err := handleNormalFile(item.LocalPath, item.RemotePath, nodeContext, sftpClient); err != nil {
					return err
				}
			}
		}

		endTime := time.Now() // 记录结束时间
		elapsedTime := endTime.Sub(startHandleTime)
		sendMsg(nodeContext, true, "执行处理结束 <--------------------------------------- 耗时："+elapsedTime.String())
		return nil
	})
}

func uploadFile(localPath string, remotePath string, nodeContext *dto.NodeContext, sftpClient *sftp.Client) error {
	// 打开本地文件
	localFile, err := os.Open(localPath)
	if err != nil {
		log.Printf("无法打开本地文件：%v\n", err)
		return fmt.Errorf("无法打开本地文件: %v", err)
	}
	defer localFile.Close()

	// 获取本地文件大小
	fileInfo, err := localFile.Stat()
	if err != nil {
		log.Printf("无法获取文件信息：%v\n", err)
		return fmt.Errorf("无法获取本地文件信息：%v", err)
	}
	fileSize := fileInfo.Size()

	// 确保远程目录存在
	remoteDir := filepath.Dir(remotePath)
	if err := createRemoteDirectory(sftpClient, remoteDir); err != nil {
		return fmt.Errorf("无法创建远程目录 %s：%v", remoteDir, err)
	}

	// 创建远程文件
	remoteFile, err := sftpClient.Create(remotePath)
	if err != nil {
		log.Printf("无法创建远程文件：%v\n", err)
		return fmt.Errorf("无法创建远程文件：%v", err)
	}
	defer remoteFile.Close()

	// 设置缓冲区大小
	bufferSize := 5 * 1024 * 1024 // 5MB
	buffer := make([]byte, bufferSize)

	// 记录已传输的字节数
	transferred := int64(0)

	// 开始传输文件
	startTime := time.Now()
	sendMsg(nodeContext, true, fmt.Sprintf("开始传输文件：%s -> %s", localFile.Name(), remoteFile.Name()))
	for {
		n, err := localFile.Read(buffer)
		if n <= 0 {
			break
		}
		if n > 0 {
			_, err := remoteFile.Write(buffer[:n])
			if err != nil {
				log.Printf("文件传输失败：%v\n", err)
				return fmt.Errorf("文件传输失败：%v", err)
			}
			transferred += int64(n)
			// 输出进度信息
			elapsedTime := time.Since(startTime).Seconds()
			progress := float64(transferred) / float64(fileSize) * 100
			speed := float64(transferred) / elapsedTime / (1024 * 1024) // MB/s
			outLog := fmt.Sprintf("%s 传输进度: %.2f%%, 已传输: %d/%d 字节, 速度: %.2f MB/s\r", localFile.Name(), progress, transferred, fileSize, speed)
			fmt.Printf(outLog)
			sendMsg(nodeContext, true, outLog)
		}
		if err == io.EOF {
			break
		}
		if err != nil {
			log.Printf("文件读取失败：%v\n", err)
			return fmt.Errorf("文件读取失败：%v", err)
		}
	}
	sendMsg(nodeContext, true, fmt.Sprintf("传输文件完成：%s -> %s", localFile.Name(), remoteFile.Name()))
	return nil
}

// 创建远程目录
func createRemoteDirectory(sftpClient *sftp.Client, remoteDir string) error {
	// 检查远程目录是否存在
	_, err := sftpClient.Stat(remoteDir)
	if os.IsNotExist(err) {
		// 目录不存在，创建目录
		if err := sftpClient.MkdirAll(remoteDir); err != nil {
			return fmt.Errorf("无法创建远程目录 %s：%v", remoteDir, err)
		}
		fmt.Printf("创建远程目录 %s 成功\n", remoteDir)
	} else if err != nil {
		return fmt.Errorf("无法检查远程目录 %s：%v", remoteDir, err)
	}
	return nil
}

func uploadDir(localPath string, remotePath string, nodeContext *dto.NodeContext, sftpClient *sftp.Client) error {
	files, err := os.ReadDir(localPath)
	if err != nil {
		fmt.Printf("无法读取目录 %s 的内容：%v\n", localPath, err)
		return err
	}

	for _, file := range files {
		if file.IsDir() {
			uploadDir(filepath.Join(localPath, file.Name()), filepath.Join(remotePath, file.Name()), nodeContext, sftpClient)
		} else {
			uploadFile(filepath.Join(localPath, file.Name()), filepath.Join(remotePath, file.Name()), nodeContext, sftpClient)
		}
	}
	return nil
}

func handleNormalFile(localPath string, remotePath string, nodeContext *dto.NodeContext, sftpClient *sftp.Client) error {
	info, err := os.Stat(localPath)
	if err != nil {
		return err
	}

	if info.IsDir() {
		// 处理目录
		if err := uploadDir(localPath, remotePath, nodeContext, sftpClient); err != nil {
			return err
		}
	} else {
		// 处理文件
		if err := uploadFile(localPath, remotePath, nodeContext, sftpClient); err != nil {
			return err
		}
	}
	return nil
}

func handleMatchFile(localPath string, remotePath string, nodeContext *dto.NodeContext, sftpClient *sftp.Client) error {
	matches, err := filepath.Glob(localPath)
	if err != nil {
		log.Printf("handleMatchFile matches err:%v\n", err)
		return err
	}
	log.Printf("handleMatchFile matches:%v\n", matches)
	for _, match := range matches {
		info, err := os.Stat(match)
		if err != nil {
			return err
		}

		name := filepath.Join(remotePath, info.Name())
		if info.IsDir() {
			// 处理目录
			if err := uploadDir(match, name, nodeContext, sftpClient); err != nil {
				return err
			}
		} else {
			// 处理文件
			if err := uploadFile(match, name, nodeContext, sftpClient); err != nil {
				return err
			}
		}
	}
	return nil
}
